package com.rongke.utils;

import java.util.HashSet;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

import com.rongke.enums.SexEnum;

/**
 * 身份证的校验
 * 
 * @author Administrator
 *
 */
public class IdCardCheck {

	private static final String REGEX_ID_CARD_18 = "^[0-9]{17}[0-9Xx]$";

	private static final String REGEX_ID_CARD_15 = "^[0-9]{15}$";

	private static final HashSet<String> AREA_SET = new HashSet<String>();

	static {
		AREA_SET.add("11");// 北京
		AREA_SET.add("12");// 天津
		AREA_SET.add("13");// 河北
		AREA_SET.add("14");// 山西
		AREA_SET.add("15");// 内蒙古
		AREA_SET.add("21");// 辽宁
		AREA_SET.add("22");// 吉林
		AREA_SET.add("23");// 黑龙江
		AREA_SET.add("31");// 上海
		AREA_SET.add("32");// 江苏
		AREA_SET.add("33");// 浙江
		AREA_SET.add("34");// 安徽
		AREA_SET.add("35");// 福建
		AREA_SET.add("36");// 江西
		AREA_SET.add("37");// 山东
		AREA_SET.add("41");// 河南
		AREA_SET.add("42");// 湖北
		AREA_SET.add("43");// 湖南
		AREA_SET.add("44");// 广东
		AREA_SET.add("45");// 广西
		AREA_SET.add("46");// 海南
		AREA_SET.add("50");// 重庆
		AREA_SET.add("51");// 四川
		AREA_SET.add("52");// 贵州
		AREA_SET.add("53");// 云南
		AREA_SET.add("54");// 西藏
		AREA_SET.add("61");// 陕西
		AREA_SET.add("62");// 甘肃
		AREA_SET.add("63");// 青海
		AREA_SET.add("64");// 宁夏
		AREA_SET.add("65");// 新疆
		AREA_SET.add("71");// 台湾
		AREA_SET.add("81");// 香港
		AREA_SET.add("82");// 澳门
		AREA_SET.add("91");// 国外
	}

	/**
	 * <p>
	 * 身份证格式强校验
	 * </p>
	 * <p>
	 * 身份证号码验证<br>
	 * 1、由十七位数字本体码和一位校验码组成。<br>
	 * 排列顺序从左至右依次为：六位数字地址码， 八位数字出生日期码，三位数字顺序码和一位数字校验码。 <br>
	 * 2、地址码(前六位数） 表示编码对象常住户口所在县(市、旗、区)的行政区划代码，按GB/T2260的规定执行。 <br>
	 * 3、出生日期码（第七位至十四位） 表示编码对象出生的年、月、日<br>
	 * 4、顺序码（第十五位至十七位） 表示在同一地址码所标识的区域范围内，对同年、同月、同日出生的人编定的顺序号，
	 * 顺序码的奇数分配给男性，偶数分配给女性。<br>
	 * 5、校验码（第十八位数） （1）十七位数字本体码加权求和公式 S = Sum(Ai * Wi), i = 0, ... , 16
	 * ，先对前17位数字的权求和 Ai:表示第i位置上的身份证号码数字值 Wi:表示第i位置上的加权因子 Wi: 7 9 10 5 8 4 2 1 6
	 * 3 7 9 10 5 8 4 2 （2）计算模 Y = mod(S, 11) （3）通过模得到对应的校验码 Y: 0 1 2 3 4 5 6 7
	 * 8 9 10 校验码: 1 0 X 9 8 7 6 5 4 3 2
	 * </p>
	 */
	public static final boolean check18IdCardNo(String idNumber) {
		if (StringUtils.isBlank(idNumber)) {
			return false;
		}
		idNumber = idNumber.trim();

		if (!checkIdNumberRegex(idNumber, REGEX_ID_CARD_18)) {
			return false;
		}

		if (!checkIdNumberArea(idNumber.substring(0, 2))) {
			return false;
		}

		if (!checkBirthday(idNumber.substring(6, 14))) {
			return false;
		}

		if (!checkIdNumberVerifyCode(idNumber)) {
			return false;
		}

		return true;
	}

	public static final boolean check15IdCardNo(String idNumber) {
		if (StringUtils.isBlank(idNumber)) {
			return false;
		}
		idNumber = idNumber.trim();

		if (!checkIdNumberRegex(idNumber, REGEX_ID_CARD_15)) {
			return false;
		}

		if (!checkIdNumberArea(idNumber.substring(0, 2))) {
			return false;
		}

		if (!checkBirthday("19" + idNumber.substring(6, 12))) {
			return false;
		}

		return true;
	}

	/**
	 * 统一18/15位的身份证校验方法<br>
	 * 无论15/18位，如果格式校验通过，统一格式化成18位返回<br>
	 * 如果格式不正确，返回null
	 * 
	 * @param idCardNo
	 * @return
	 */
	public static String checkValidIdCardNo(String idCardNo) {
		if (StringUtil.isEmpty(idCardNo)) {
			return null;
		}

		idCardNo = idCardNo.trim();
		if (idCardNo.length() == 18) {
			if (check18IdCardNo(idCardNo)) {
				// 校验通过，直接返回
				return idCardNo;
			} else {
				return null;
			}
		}

		if (idCardNo.length() == 15) {
			if (check15IdCardNo(idCardNo)) {
				// 校验通过，直接返回
				return convertFifteenToEighteen(idCardNo);
			} else {
				return null;
			}
		}

		return null;
	}

	/**
	 * 获取18位格式正确的身份证号码中的出生日期
	 * 
	 * @param idCardNo
	 * @return
	 */
	public static String getBirthday(String idCardNo) {
		return idCardNo.substring(6, 14);
	}

	/**
	 * 获取18位格式正确的身份证号码中的性别
	 * 
	 * @param idCardNo
	 * @return
	 */
	public static SexEnum getSex(String idCardNo) {
		String sexCode = String.valueOf(idCardNo.charAt(16));
		int sexInt = Integer.parseInt(sexCode);
		return sexInt % 2 == 0 ? SexEnum.female : SexEnum.male;
	}

	/**
	 * 身份证正则校验
	 */
	private static boolean checkIdNumberRegex(String idNumber, String regStr) {
		return Pattern.matches(regStr, idNumber);
	}

	/**
	 * 身份证地区码检查
	 */
	private static boolean checkIdNumberArea(String idNumberArea) {
		if (AREA_SET.contains(idNumberArea)) {
			return true;
		}
		return false;
	}

	/**
	 * 将15位身份证转换为18位
	 */
	private static String convertFifteenToEighteen(String idNumber) {
		if (15 != idNumber.length()) {
			return idNumber;
		}
		idNumber = idNumber.substring(0, 6) + "19" + idNumber.substring(6, 15);
		idNumber = idNumber + getVerifyCode(idNumber);
		return idNumber;
	}

	/**
	 * 根据身份证前17位计算身份证校验码
	 */
	private static String getVerifyCode(String idNumber) {
		String[] ValCodeArr = { "1", "0", "X", "9", "8", "7", "6", "5", "4",
				"3", "2" };
		String[] Wi = { "7", "9", "10", "5", "8", "4", "2", "1", "6", "3", "7",
				"9", "10", "5", "8", "4", "2" };

		int sum = 0;
		for (int i = 0; i < 17; i++) {
			sum = sum + Integer.parseInt(String.valueOf(idNumber.charAt(i)))
					* Integer.parseInt(Wi[i]);
		}
		return ValCodeArr[sum % 11];
	}

	/**
	 * 身份证出生日期
	 */
	private static boolean checkBirthday(String idNumberBirthdayStr) {
		return DateUtils.checkDateStrValid(idNumberBirthdayStr);
	}

	/**
	 * 身份证校验码检查
	 */
	private static boolean checkIdNumberVerifyCode(String idNumber) {
		return getVerifyCode(idNumber).equalsIgnoreCase(idNumber.substring(17));
	}

}